package com.eyas.im.protocol.decoder.delegation;

import com.eyas.im.protocol.decoder.Decoder;
import com.eyas.im.protocol.decoder.IMIdentifierDecoder;
import com.eyas.im.protocol.decoder.delegation.exception.InvalidProtocolException;
import com.eyas.im.protocol.protocol.Protocol;
import com.eyas.im.protocol.protocol.StringProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
 * 通信协议选举器，用于解析TCP datapacket的数据，判断后续要被那种协议处理器处理，目前支持的协议处理器如下：
 * 1、用户身份协议
 * 2、聊天内容协议
 *
 * 注意：需要解决TCP半包、粘包的问题；线程安全
 */
public class DelegationDecoder implements Decoder<Protocol> {
    private final static Logger logger = LoggerFactory.getLogger(DelegationDecoder.class);
    private byte[] protocolHeaderCache = new byte[2]; //存储协议头信息，防止协议头被拆包造成问题
    private byte[] protocolBreakerCache = new byte[2]; //存储协议尾信息，防止半包问题
    private Decoder<? extends Protocol> recentDecoder; //发送半包后，记录接下来处理数据时使用的协议处理器
    @Override
    public Protocol decode(byte[] bytes) throws IOException {
        if(protocolHeaderCache[0]==2) {
            //TCP半包发生
            if(protocolHeaderCache[1]!=0) {
                //半包发生时，协议类型已经确认，此时只要持续不断的发送后续数据就可以了
                if(recentDecoder==null) {
                    logger.warn("半包处理器为空，通信数据可能有问题[{}]",bytes);
                    return null;
                }
                this.dispatch(bytes);
            }else {
                //半包发生时，只读取到了protocol header的第一个字节，需要在此次方法中，判断后续的协议处理器类型
                try {
                    this.recentDecoder = DelegationDecoder.delegateProtocolProcessor(bytes[0]);
                    if(bytes.length>1) {
                        byte[] dispatchBytes = new byte[bytes.length-1];
                        System.arraycopy(bytes,1,dispatchBytes,0,bytes.length-1);
                        this.dispatch(dispatchBytes);
                    }
                } catch (InvalidProtocolException e) {
                    //协议不符合header要求，变量bytes，找到第一次header出现的位置，前面的字节丢弃
                    this.resetProtocolHeader();
                    int i=0;
                    for(;i<bytes.length;i++) {
                        if(bytes[i]==Decoder.PROTOCOL_HEADER_START) {
                            this.protocolHeaderCache[0] = Decoder.PROTOCOL_HEADER_START;
                            logger.warn("丢弃了{}个字节的数据",i);
                            byte[] validBytes = new byte[bytes.length-i-1];
                            System.arraycopy(bytes,i+1,validBytes,0,bytes.length-i-1);
                            this.decode(validBytes);
                            break;
                        }
                    }
                    //此次decode没有有效的header信息，不做任何处理
                }
            }
        }else {
            //等待protocol header的到来，只要不来，无论bytes的内容是什么，都直接丢弃
        }
        return null;
    }

    /**
     * 分发数据
     * @param bytes
     */
    private void dispatch(byte[] bytes) throws IOException {
        if(this.protocolBreakerCache[0]==Decoder.PROTOCOL_BREAKER[0]) { //协议结束部分只需要处理tcp半包问题即可
            if(bytes[0]==Decoder.PROTOCOL_BREAKER[1]) {//获取到中断协议
                this.resetDecoder(); //重置处理器，等待接受下一批数据
            }
            if(bytes.length>1) {
                byte[] surplusBytes = new byte[bytes.length-1];
                System.arraycopy(bytes,1,surplusBytes,0,bytes.length-1);
                this.decode(surplusBytes);
            }
        }else {
            int i = 0;
            for(;i<bytes.length;i++) {
                if(bytes[i]==10) {
                    this.protocolBreakerCache[0]=Decoder.PROTOCOL_BREAKER[0];
                }
                if(bytes[i]==Decoder.PROTOCOL_BREAKER[1]&& this.protocolBreakerCache[0]==Decoder.PROTOCOL_BREAKER[0]) {
                    //中断标志位
                    this.resetDecoder(); //重置处理器，等待接受下一批数据
                    break;
                }
            }
            byte[] delegateBytes = new byte[i+1];
            System.arraycopy(bytes,0,delegateBytes,0,i+1);
            this.recentDecoder.decode(delegateBytes);

            //获取剩余的数据，重新传输给decode方法
            if(bytes.length-i-1>0) {
                byte[] surplusBytes = new byte[bytes.length-i-1];
                System.arraycopy(bytes,i+1,surplusBytes,0,surplusBytes.length);
                this.decode(surplusBytes);
            }
        }
    }

    private void resetProtocolHeader() {
        this.protocolHeaderCache[0] = this.protocolHeaderCache[1] = 0;
        this.recentDecoder = null;
    }

    private void resetProtocolBreaker() {
        this.protocolBreakerCache[0] = this.protocolBreakerCache[1] = 0;
    }

    private void resetDecoder() {
        this.resetProtocolHeader();
        this.resetProtocolBreaker();
    }

    /**
     * 根据delegationKey选择合适的协议处理器
     * @param delegationKey
     * @return
     */
    private static Decoder<?> delegateProtocolProcessor(byte delegationKey) throws InvalidProtocolException {
        switch (delegationKey) {
            case 1:
                return new IMIdentifierDecoder();
            default:
                throw new InvalidProtocolException();
        }
    }
}
